/**
 * This file is part of iSecurePasswords.
 *
 * iSecurePasswords is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * iSecurePasswords is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * iSecurePasswords. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Matthew MacGregor Created: 2012 iSecurePasswords, copyright 2013
 * Sudolink, LLC
 *
 */
package com.sudolink.isp.ui;

import com.sudolink.isp.ui.locktimer.UnlockFrame;
import com.sudolink.crypto.CryptoException;
import com.sudolink.cryptodata.CryptInfoBundle;
import com.sudolink.cryptodata.ISecureData;
import com.sudolink.cryptodata.SecureData;
import com.sudolink.cryptodata.SecureDataManager;
import com.sudolink.io.IStore;
import com.sudolink.io.Store;
import com.sudolink.isp.app.Application;
import com.sudolink.isp.cryptodata.SecureDataHelpers;
import com.sudolink.isp.ui.locktimer.IManageLocks;
import com.sudolink.isp.ui.locktimer.LockManager;
import com.sudolink.isp.ui.locktimer.LockTimer;
import java.awt.Window;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import javax.swing.JOptionPane;

public class MainWindowManager {

    private final MainWindow parentFrame;
    private boolean showArchivedEntries = false;
    // Maintains a list of windows that have been spawned by the parent.
    private final List<Window> childFrames;
    private final IManageLocks lockManager;
    
    @SuppressWarnings("OverridableMethodCallInConstructor")
    public MainWindowManager(MainWindow frame) {
        parentFrame = frame;
        childFrames = new ArrayList<>();
        
        long lockTimeout = Application.getLockTimeoutInSeconds();
        LockTimer lockTimer = new LockTimer();
        lockTimer.setSecondsUntilLocked((int)lockTimeout); 
        lockTimer.setListener(() -> {
            lockAll();
        });
        lockManager = new LockManager(lockTimer);
        lockManager.register(parentFrame);
        lockManager.start();
    }

    /**
     * Resets the lock timer.
     */
    public void resetLock() {
        lockManager.restart();
    }
    
    /**
     * Locks all UI.
     */
    public void lockAll() {
        UnlockFrame uw = new UnlockFrame(lockManager);
        uw.setLocationRelativeTo(parentFrame);
        uw.setVisible(true);
        Application.refreshLastUserInteractionTime();
        
        lockManager.lockAll();
    }
    
    /**
     * Reveals if the controller has been set to show archived items or to hide
     * them.
     *
     * @return True if archived items will be shown.
     */
    public boolean willShowArchivedEntries() {
        return this.showArchivedEntries;
    }

    /**
     * Sets whether or not the controller will show archived items.
     *
     * @param isShow True if archived items should be revealed.
     */
    public void setShowArchivedEntries(boolean isShow) {
        showArchivedEntries = isShow;
    }
    
    /**
     * Searches for the string parameter in the EntryName and Url fields of the
     * list of entries.
     *
     * @param list This is the list of entries.
     * @param stringToMatch The string that is being searched for.
     * @return An array of entries that have not been excluded.
     */
    public ISecureData[] search(ISecureData[] list, String stringToMatch) {
        CryptInfoBundle bundle = Application.getPrivateKeys();
        List<ISecureData> newList = new ArrayList<>();
        for (ISecureData item : list) {
            String searchedString = item.get(ISecureData.ENTRYNAME, bundle)
                    + item.get(ISecureData.URL, bundle);
            boolean isArchived = SecureDataHelpers.getBoolean(item, ISecureData.IS_ARCHIVED, false);
            //If the entry isn't archived, or if hiding archived items is switched off
            if (!isArchived || (isArchived && willShowArchivedEntries())) {
                //This is a symptom of combining this into one method, because if
                //there is no string or the strin is empty we're not trying to do
                //a search. Otherwise, apply the search.
                if (stringToMatch == null || stringToMatch.isEmpty()) {
                    newList.add(item);
                } else if (searchedString.toLowerCase().contains(stringToMatch.toLowerCase())) {
                    newList.add(item);
                }

            }
        }

        return newList.toArray(new ISecureData[]{});
    }

    /**
     * Sort the list of items into the default order. It uses the default
     * comparator.
     *
     * @param list
     */
    public void sort(ISecureData[] list) {
        sort(list, COMPARATOR);
    }

    /**
     * Sorts, allowing a custom comparator rather than the default.
     *
     * @param list
     * @param comparator
     */
    public void sort(ISecureData[] list, Comparator<ISecureData> comparator) {
        Arrays.sort(list, comparator);
    }

    /**
     * Adds a new data item to the list. This method is the equivalent of calling
     * addElement( iRefreshData, null );
     * 
     * @param iRefreshData 
     */
    public void addElement(IRefreshData iRefreshData) {
        addElement(iRefreshData, null);
    }
    
    /**
     * Adds a new or existing data item to the list. If the data parameter is
     * provided, this method will use it directly. If the data parameter is null,
     * a new (empty) element will be created. Use the single-param overload for
     * creating new elements. Use the two-param overload for opening existing
     * elements.
     * 
     * @param iRefreshData
     * @param data 
     */
    public void addElement(IRefreshData iRefreshData, SecureData data) {
        if( data == null ) {
            data = new SecureData();
            Application.getSecureDataManager().add(data);
        }
 
        EntryEditForm form = new EntryEditForm(data, iRefreshData);
        lockManager.register(form);
        form.setVisible(true);
    }

    public void deleteElement(SecureData sde) throws IOException, CryptoException {
        int result = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete this entry?");
        if (result == JOptionPane.OK_OPTION) {
            SecureDataManager mgr = Application.getSecureDataManager();
            CryptInfoBundle bundle = Application.getPrivateKeys();
            IStore store = new Store(Application.getPathToDatabase());
            store.shouldOverwrite(true);
            mgr.delete(sde);
            mgr.saveAll(store, bundle);
        }
    }
    
    /**
     * This is used to sort a list of ISecureData objects. It uses the
     * SecureData implementation of toString().
     */
    private static final Comparator<ISecureData> COMPARATOR = (ISecureData o1, ISecureData o2) -> {
        String s1 = o1.toString().toLowerCase();
        String s2 = o2.toString().toLowerCase();
        
        return s1.compareTo(s2);
    }; // TODO: Improve this to remove the dependency on toString()
 
}
